import pyttsx3
import argparse
import os
import glob
import time
from typing import Optional, List

# 示例用法:
# 批量转换（每次新引擎）: python english_text_to_wav.py -p "./p" -e "txt" -v 0 -r 120
# 单个文件转换（每次新引擎）: python english_text_to_wav.py -f "./test.txt" -v 0 -r 120 -o "./output.wav"

class EnglishTextToWav:
    def __init__(self):
        """初始化配置（引擎实例每次生成时动态创建）"""
        self.default_config = {
            "rate": 120,  # 英语默认语速
            "output_dir": ".",
            "file_extension": "txt",
            "prefer_english_voice": True,
            "engine_retry_count": 2  # 引擎初始化失败时重试次数
        }

    def _init_new_engine(self, voice_index: Optional[int] = None, rate: Optional[int] = None) -> pyttsx3.Engine:
        """
        创建新的TTS引擎实例并配置
        每次生成音频都调用此方法，避免引擎状态污染
        """
        engine = None
        # 引擎初始化重试机制（应对偶尔的初始化失败）
        for retry in range(self.default_config["engine_retry_count"]):
            try:
                # 每次都创建全新的引擎实例
                engine = pyttsx3.init()
                # 筛选英语语音包
                english_voices = []
                all_voices = engine.getProperty("voices")
                for voice in all_voices:
                    if hasattr(voice, 'language'):
                        lang_info = voice.language.lower()
                    elif hasattr(voice, 'languages') and voice.languages:
                        lang_info = voice.languages[0].lower()
                    else:
                        lang_info = ""
                    if "en" in lang_info:
                        english_voices.append(voice)
                
                # 配置语音包
                if voice_index is not None and 0 <= voice_index < len(english_voices):
                    selected_voice = english_voices[voice_index]
                    engine.setProperty("voice", selected_voice.id)
                    print(f"  🔊 使用语音包：{selected_voice.name}")
                else:
                    if english_voices:
                        default_voice = english_voices[0]
                        engine.setProperty("voice", default_voice.id)
                        print(f"  🔊 自动选择语音包：{default_voice.name}")
                    else:
                        print("  ⚠️  无可用英语语音包，使用系统默认")
                
                # 配置语速
                target_rate = rate if rate is not None else self.default_config["rate"]
                if 80 <= target_rate <= 240:
                    engine.setProperty("rate", target_rate)
                    print(f"  ⏱️  语速设置为：{target_rate}")
                else:
                    target_rate = self.default_config["rate"]
                    engine.setProperty("rate", target_rate)
                    print(f"  ⏱️  语速范围80-240，使用默认值：{target_rate}")
                
                # 确保音量正常（避免静音问题）
                engine.setProperty("volume", 1.0)
                return engine
            
            except Exception as e:
                print(f"  ❌ 第{retry+1}次引擎初始化失败：{str(e)}")
                if retry < self.default_config["engine_retry_count"] - 1:
                    time.sleep(0.5)  # 重试前短暂间隔
                continue
        
        # 多次重试失败后抛出异常
        raise RuntimeError("TTS引擎初始化失败，无法生成音频")

    def list_english_voices(self) -> None:
        """列出所有可用英语语音包（临时创建引擎）"""
        try:
            # 临时创建引擎仅用于获取语音列表
            temp_engine = pyttsx3.init()
            all_voices = temp_engine.getProperty("voices")
            english_voices = []
            
            for voice in all_voices:
                if hasattr(voice, 'language'):
                    lang_info = voice.language.lower()
                elif hasattr(voice, 'languages') and voice.languages:
                    lang_info = voice.languages[0].lower()
                else:
                    lang_info = ""
                if "en" in lang_info:
                    english_voices.append(voice)
            
            # 释放临时引擎资源
            temp_engine.stop()
            del temp_engine
            
            if not english_voices:
                print("⚠️  未检测到英语语音包，请先安装")
                return
        
            print("\n📢 可用英语语音包列表：")
            for idx, voice in enumerate(english_voices):
                if hasattr(voice, 'language'):
                    lang_detail = voice.language
                elif hasattr(voice, 'languages') and voice.languages:
                    lang_detail = voice.languages[0]
                else:
                    lang_detail = "unknown"
                
                name = voice.name if hasattr(voice, "name") else "Unknown"
                gender = "Male" if "male" in voice.id.lower() else "Female" if "female" in voice.id.lower() else "Unknown"
                accent = "美式" if "en-us" in lang_detail.lower() else "英式" if "en-gb" in lang_detail.lower() else "其他"
                
                print(f"  索引{idx}：{name} | {accent}（{lang_detail}） | 性别：{gender}")
        
        except Exception as e:
            print(f"❌ 获取语音列表失败：{str(e)}")

    def clean_text(self, text: str) -> str:
        """仅清理文本（无停顿处理，仅去除多余空白）"""
        text = text.strip()
        # 将多个换行/空格合并为单个，避免无意义空白影响朗读
        text = ' '.join(text.split())
        return text

    def read_text_file(self, file_path: str) -> Optional[str]:
        """读取文本文件内容（支持UTF-8编码）"""
        if not os.path.exists(file_path):
            print(f"❌ 错误：文件不存在 - {file_path}")
            return None
            
        file_ext = os.path.splitext(file_path)[1].lower()
        if file_ext not in [".txt", ".text"]:
            print(f"⚠️  警告：建议使用.txt文件，继续尝试读取 {file_path}")
            
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read().strip()
                if not content:
                    print(f"❌ 错误：文件内容为空 - {file_path}")
                    return None
                print(f"✅ 成功读取文件：{file_path}（{len(content)}字符）")
                return content
        except UnicodeDecodeError:
            print(f"❌ 错误：文件编码不是UTF-8，请转换编码后重试 - {file_path}")
            return None
        except Exception as e:
            print(f"❌ 读取文件失败 {file_path}：{str(e)}")
            return None

    def file_to_wav(self, file_path: str, output_path: Optional[str] = None, 
                   voice_index: Optional[int] = None, rate: Optional[int] = None) -> bool:
        """
        将单个文本文件转换为无停顿的WAV音频
        每次调用都创建新引擎实例
        """
        # 读取文件内容
        text = self.read_text_file(file_path)
        if not text:
            return False
            
        # 检查中文标点（仅提示）
        if any(p in text for p in ["，", "。", "、"]):
            print("⚠️  检测到中文标点，建议替换为英文标点以优化朗读流畅度")
        
        # 清理文本
        clean_text = self.clean_text(text)
        if not clean_text:
            print(f"❌ 错误：清理后无有效文本 - {file_path}")
            return False
        
        # 确定输出路径
        if not output_path:
            base_name = os.path.splitext(file_path)[0]
            output_path = f"{base_name}.wav"
        elif os.path.isdir(output_path):
            file_name = os.path.splitext(os.path.basename(file_path))[0]
            output_path = os.path.join(output_path, f"{file_name}.wav")
        
        # 确保输出目录存在
        output_dir = os.path.dirname(output_path)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir, exist_ok=True)
        
        # 生成音频（每次新引擎）
        try:
            print(f"🔄 初始化新TTS引擎...")
            # 1. 创建新引擎实例并配置
            engine = self._init_new_engine(voice_index=voice_index, rate=rate)
            
            # 2. 生成音频（无停顿，直接传入完整文本）
            # engine.say(clean_text)
            engine.save_to_file(clean_text, output_path)
            engine.runAndWait()
            
            # 3. 强制释放引擎资源（关键：避免内存泄漏和状态残留）
            engine.stop()
            del engine
            print(f"🔌 已释放当前引擎资源")
            
            # 4. 验证文件有效性
            if os.path.exists(output_path) and os.path.getsize(output_path) > 100:
                abs_path = os.path.abspath(output_path)
                print(f"✅ 转换成功：{abs_path}")
                print(f"   📏 大小：{os.path.getsize(output_path)/1024:.2f} KB")
                return True
            else:
                print(f"❌ 生成失败：文件为空或未创建 - {output_path}")
                # 清理无效文件
                if os.path.exists(output_path):
                    os.remove(output_path)
                return False
        
        except Exception as e:
            print(f"❌ 转换出错 {file_path}：{str(e)}")
            import traceback
            print(f"   📝 异常详情：{traceback.format_exc()}")
            # 出错后确保引擎资源释放
            if 'engine' in locals():
                engine.stop()
                del engine
            return False

    def batch_convert_by_prefix(self, prefix: str, ext: str = "txt", output_dir: Optional[str] = None,
                               voice_index: Optional[int] = None, rate: Optional[int] = None) -> None:
        """
        批量转换具有相同前缀的文件（每次新引擎）
        """
        # 构建文件搜索模式
        pattern = f"{prefix}*.{ext}"
        files = glob.glob(pattern)
        
        if not files:
            print(f"❌ 未找到匹配的文件：{pattern}")
            return
            
        # 按文件名排序（确保顺序一致）
        files.sort()
        
        print(f"\n📋 找到 {len(files)} 个匹配文件，开始批量转换（每次新引擎）：")
        success_count = 0
        
        for idx, file_path in enumerate(files):
            print(f"\n=== 处理第{idx+1}/{len(files)}个文件 ===")
            # 批量转换时添加短暂间隔，避免系统资源竞争（macOS关键优化）
            if idx > 0:
                wait_time = 0.8  # 间隔0.8秒，可根据系统性能调整
                print(f"⌛ 等待{wait_time}秒，避免资源竞争...")
                time.sleep(wait_time)
            
            # 确定当前文件输出路径
            if output_dir:
                file_name = os.path.splitext(os.path.basename(file_path))[0]
                current_output = os.path.join(output_dir, f"{file_name}.wav")
            else:
                current_output = None
            
            # 每次转换都调用file_to_wav（内部会创建新引擎）
            if self.file_to_wav(file_path, current_output, voice_index, rate):
                success_count += 1
        
        print(f"\n📊 批量转换完成：成功 {success_count}/{len(files)}")
        # 提示结果路径
        if output_dir:
            print(f"📁 所有成功文件已保存到：{os.path.abspath(output_dir)}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="英语文本文件转WAV音频工具（每次新引擎+无额外停顿）")
    # 批量转换参数
    parser.add_argument("-p", "--prefix", help="文件前缀（用于批量转换，如\"./p\"）")
    parser.add_argument("-e", "--extension", default="txt", help="文件扩展名（默认：txt）")
    # 单个文件转换参数
    parser.add_argument("-f", "--file", help="单个英语文本文件路径（.txt）")
    # 通用参数
    parser.add_argument("-o", "--output", help="输出文件或目录路径（默认：与源文件同目录同名）")
    parser.add_argument("-r", "--rate", type=int, help="语速（80-240，默认120）")
    parser.add_argument("-v", "--voice-index", type=int, help="语音包索引（先用 -l 查看）")
    parser.add_argument("-l", "--list-voices", action="store_true", help="列出所有英语语音包")
    
    args = parser.parse_args()

    # 验证参数合法性
    if not args.list_voices and not args.file and not args.prefix:
        parser.error("必须提供 -f（单个文件）或 -p（批量前缀），或使用 -l 列出语音包")

    converter = EnglishTextToWav()

    # 列出语音包（临时引擎）
    if args.list_voices:
        converter.list_english_voices()
        exit(0)

    # 执行转换
    if args.file:
        # 单个文件转换（每次新引擎）
        converter.file_to_wav(
            file_path=args.file,
            output_path=args.output,
            voice_index=args.voice_index,
            rate=args.rate
        )
    elif args.prefix:
        # 批量转换（每次新引擎+间隔优化）
        converter.batch_convert_by_prefix(
            prefix=args.prefix,
            ext=args.extension,
            output_dir=args.output,
            voice_index=args.voice_index,
            rate=args.rate
        )